home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 July: Mac OS SDK / Dev.CD Jul 96 SDK / Dev.CD Jul 96 SDK1.toast / Development Kits (Disc 1) / OpenDoc Development Framework / ODFDev / ODF / Found / FWExcLib / Sources / FWExcImp.cpp
Encoding:
Text File  |  1996-04-25  |  26.9 KB  |  839 lines  |  [TEXT/MPS ]

  1. //========================================================================================
  2. //
  3. //    File:                FWExcImp.cpp
  4. //    Release Version:    $ ODF 1 $
  5. //
  6. //    Copyright:    (c) 1993 - 1996 by Apple Computer, Inc., all rights reserved.
  7. //
  8. //========================================================================================
  9.  
  10. #ifndef FW_NATIVE_EXCEPTIONS
  11.  
  12. #include "FWFound.hpp"
  13.  
  14. #ifndef FWEXCIMP_H
  15. #include "FWExcImp.h"
  16. #endif
  17.  
  18. #ifndef FWTHRDGD_H
  19. #include "FWThrdGd.h"
  20. #endif
  21.  
  22. #ifndef FWDEBUG_H
  23. #include "FWDebug.h"
  24. #endif
  25.  
  26. #ifdef FW_BUILD_MAC
  27. #include <Gestalt.h>
  28. #endif
  29.  
  30. #ifdef FW_BUILD_MAC
  31. #pragma segment slexcept
  32. #endif
  33.  
  34. #ifdef FW_DEBUG
  35.     #include <stdio.h>
  36.     #define _FW_NAME_PARAMETER ,char* name
  37. #else
  38.     #define _FW_NAME_PARAMETER
  39. #endif
  40.  
  41. //========================================================================================
  42. // struct FW_SExceptionGlobals
  43. //========================================================================================
  44.  
  45. struct FW_SExceptionGlobals
  46. {
  47.     FW_SPrivTryBlockContext*    fCurrentContext;
  48.     FW_SPrivWatcher*             fWatchList;
  49.     FW_SPrivDeleteElem*            fStackBottom;    // These are pointers into the delete stack,
  50.     FW_SPrivDeleteElem*            fStackTop;        // not pointers into the function call stack
  51.     FW_SPrivDeleteElem*            fStackLimit;
  52.  
  53.     void*                        fExceptionBuffer;
  54.     size_t                        fExceptionBufferSize;
  55.     
  56.     FW_SPrivExceptionInfo        fCaughtException;
  57.     FW_SPrivExceptionInfo         fThrownException;
  58.     
  59.     FW_Boolean                    fIsUnwinding;
  60.     
  61.     void*                        fCallStackBase;
  62.     void*                        fProcessStackBase;
  63. };
  64.  
  65. static FW_SExceptionGlobals     gMainThreadExceptionData = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  66. static FW_SExceptionGlobals*    gExceptionData = &gMainThreadExceptionData;
  67.  
  68. // FW_gInStaticInit is initialized to a special magic number before static initialization 
  69. // here.  It should be cleared by the application main or shared library initialization
  70. // routine (CFMInit) immediately after static initialization has been performed, and before
  71. // the first try block or construction of an autodestruct object with auto scope or
  72. // dynamic scope.  Note that this mechanism precludes placing
  73. // this exception emulation system in a shared library that other clients could use.
  74. // Currently, every shared library component must statically link in this eumulation
  75. // system.  However, we did have this system working from a shared library, and the
  76. // use of FW_gInStaticInit is the only thing that we have done since then that would 
  77. // prevent it from moved back into a shared library. [JEL, ODF R1, 3/21/96]
  78.  
  79. const long kMagicCookie = 'jiml';
  80. long FW_gInStaticInit = kMagicCookie;
  81.  
  82. void* FW_gPrivVolatileKludge = 0;
  83.  
  84. //========================================================================================
  85. // Key Utility Functions
  86. //========================================================================================
  87.  
  88. static long AddressIsOnStack(void* p)
  89. {
  90.     long local;
  91.     FW_PRIV_ASSERT(gExceptionData);
  92.     FW_PRIV_ASSERT(gExceptionData->fCurrentContext);
  93.     return (p > &local) && p <= gExceptionData->fCallStackBase;
  94. }
  95.  
  96. static long IsCurrentSubObject(void* p)
  97. {
  98.     register FW_SPrivWatcher* w = gExceptionData->fWatchList;
  99.     return w && p>=w->fObject && p<w->fLimit;
  100. }
  101.  
  102. static long IsCurrentObject(void* p, void* limit)
  103. {
  104.     register FW_SPrivWatcher* w = gExceptionData->fWatchList;
  105.     return w && p==w->fObject && limit==w->fLimit;
  106. }
  107.  
  108. static long TrackThisObject(void* object)
  109. {
  110.     FW_PRIV_ASSERT(gExceptionData->fCurrentContext);
  111.     return AddressIsOnStack(object) || IsCurrentSubObject(object);
  112. }
  113.  
  114. static void DoTerminate()
  115. {
  116.     // If we get here, we either have a throw with no try block in scope,
  117.     // or a rethrow with no exception in scope.
  118.     extern void terminate();
  119.     FW_DEBUG_MESSAGE("Exception error, calling terminate()");
  120. #if defined(__SC__) || defined(__MRC__)
  121.     // Symantec & MrC currently don't have terminate() in their runtime library.
  122.     ExitToShell();
  123. #else
  124.     terminate();
  125. #endif
  126. }
  127.  
  128. static void InitializeExceptionGlobals(FW_SExceptionGlobals* globals, void* bottom)
  129. {
  130.     const int kInitialStackSize = 1000;
  131.     const int kExceptionBufferSize = 256;
  132.  
  133.     globals->fProcessStackBase = LMGetCurStackBase();
  134.     globals->fCallStackBase = (globals == &gMainThreadExceptionData) ? globals->fProcessStackBase : bottom;
  135.     globals->fStackTop = globals->fStackBottom = (FW_SPrivDeleteElem*)FW_PrimitiveAllocateBlock(kInitialStackSize*sizeof(FW_SPrivDeleteElem));
  136.     globals->fStackLimit = globals->fStackBottom + kInitialStackSize;
  137.  
  138.     globals->fExceptionBuffer = FW_PrimitiveAllocateBlock(kExceptionBufferSize);
  139.     globals->fExceptionBufferSize = kExceptionBufferSize;
  140. }
  141.  
  142. static void FinalizeExceptionGlobals(FW_SExceptionGlobals* globals)
  143. {
  144.     FW_PrimitiveFreeBlock(globals->fStackBottom);
  145.     globals->fStackTop = globals->fStackBottom = 0;
  146.  
  147.     FW_PrimitiveFreeBlock(globals->fExceptionBuffer);
  148.     globals->fExceptionBuffer = 0;
  149. }
  150.  
  151. //========================================================================================
  152. // DeleteElement Functions
  153. //========================================================================================
  154.  
  155. static void DeleteElem_DestroyObject(FW_SPrivDeleteElem* elem)
  156. {
  157.     FW_PRIV_ASSERT(gExceptionData);
  158.     FW_PRIV_ASSERT(gExceptionData->fIsUnwinding);
  159.     FW_PRIV_ASSERT(elem);
  160.     FW_PRIV_ASSERT(elem->fDestroyer);
  161.     FW_PRIV_ASSERT(elem->fObject);
  162.     (*elem->fDestroyer)(elem->fObject);
  163.     FW_PRIV_ASSERT(!elem->fObject);
  164. }
  165.  
  166. //========================================================================================
  167. // ExceptionInfo Functions
  168. //========================================================================================
  169.  
  170. static void ExceptionInfo_Clear(FW_SPrivExceptionInfo& exception)
  171. {
  172.     exception.fAddress = 0;
  173.     
  174.     // The remainder don't have to be cleared, but we do so anyway
  175.     exception.fSize = 0;
  176.     exception.fClass = 0;
  177.     exception.fDestroyer = 0;
  178.     exception.fCloner = 0;
  179. }
  180.  
  181. static void ExceptionInfo_DestroyException(FW_SPrivExceptionInfo& exception)
  182. {
  183.     FW_PRIV_ASSERT(exception.fDestroyer);
  184.     FW_PRIV_ASSERT(exception.fAddress);
  185.     (*exception.fDestroyer)(exception.fAddress);
  186.     ExceptionInfo_Clear(exception);
  187. }
  188.  
  189. static void ExceptionInfo_Set(FW_SPrivExceptionInfo& exception,
  190.                                 void* address, 
  191.                                 size_t size, 
  192.                                 FW_SClassInfoPtr classPtr, 
  193.                                 FW_PrivDestroyProc destroyer,
  194.                                 FW_PrivCloneProc cloner)
  195. {
  196.     exception.fAddress = address;
  197.     exception.fSize = size;
  198.     exception.fClass = classPtr;
  199.     exception.fDestroyer = destroyer;
  200.     exception.fCloner = cloner;
  201. }
  202.  
  203. //========================================================================================
  204. // DeleteStack Functions
  205. //========================================================================================
  206.  
  207. static void DeleteStack_ResetTop()
  208. {
  209.     FW_PRIV_ASSERT(gExceptionData);
  210.     FW_PRIV_ASSERT(gExceptionData->fStackTop);
  211.     FW_PRIV_ASSERT(gExceptionData->fStackBottom);
  212.     FW_PRIV_ASSERT(gExceptionData->fCurrentContext);
  213.     FW_SPrivDeleteElem* elem = gExceptionData->fStackTop-1;
  214.     FW_SPrivDeleteElem* last = gExceptionData->fStackBottom
  215.                              + gExceptionData->fCurrentContext->fDeleteStackLevel;
  216.     while (elem>=last && !elem->fObject)
  217.         --elem;
  218.     gExceptionData->fStackTop = elem+1;
  219. }
  220.  
  221. static void DeleteStack_Push(void* object, FW_PrivDestroyProc destroyer _FW_NAME_PARAMETER)
  222. {
  223.     FW_PRIV_ASSERT(gExceptionData);
  224.     FW_PRIV_ASSERT(gExceptionData->fStackBottom);
  225.     FW_PRIV_ASSERT(gExceptionData->fStackTop);
  226.     FW_PRIV_ASSERT(gExceptionData->fStackTop < gExceptionData->fStackLimit);
  227.     gExceptionData->fStackTop->fObject = object;
  228.     gExceptionData->fStackTop->fDestroyer = destroyer;
  229. #ifdef FW_DEBUG
  230.     gExceptionData->fStackTop->fName = name;
  231.     gExceptionData->fStackTop->fHeapAllocated = IsCurrentSubObject(object);
  232.     if (gExceptionData->fStackTop->fHeapAllocated)
  233.     {
  234.         register FW_SPrivWatcher* w = gExceptionData->fWatchList;
  235.         w->fDeleteElems++;
  236.     }
  237. #endif
  238.     gExceptionData->fStackTop++;
  239. }
  240.  
  241. static void DeleteStack_ClearHeapObjectElements()
  242. {
  243.     // Called as last step of FW_NEW
  244.     FW_PRIV_ASSERT(gExceptionData);
  245.     FW_PRIV_ASSERT(gExceptionData->fStackBottom);
  246.     FW_PRIV_ASSERT(gExceptionData->fStackTop);
  247.     FW_PRIV_ASSERT(gExceptionData->fStackTop < gExceptionData->fStackLimit);
  248.     FW_PRIV_ASSERT(gExceptionData->fWatchList);
  249.     FW_PRIV_ASSERT(gExceptionData->fCurrentContext);
  250.     FW_SPrivWatcher* w = gExceptionData->fWatchList;
  251.     FW_SPrivDeleteElem* elem = gExceptionData->fStackTop-1;
  252.     FW_SPrivDeleteElem* last = gExceptionData->fStackBottom
  253.                              + gExceptionData->fCurrentContext->fDeleteStackLevel;
  254.     
  255.     while (elem>=last)
  256.     {
  257.         if (elem->fObject >= w->fObject && elem->fObject < w->fLimit)
  258.         {
  259.             FW_PRIV_ASSERT(elem->fHeapAllocated);
  260.             elem->fObject = 0;
  261. #ifdef FW_DEBUG
  262.             w->fDeleteElems--;
  263. #endif
  264.         }
  265.         --elem;
  266.     }
  267.     FW_PRIV_ASSERT(w->fDeleteElems == 0);
  268.     DeleteStack_ResetTop();
  269. }
  270.  
  271. //========================================================================================
  272. // TryBlockContext functions
  273. //========================================================================================
  274.  
  275. static void TryBlockContext_UnwindStack(FW_SPrivTryBlockContext* context)
  276. {
  277.     FW_PRIV_ASSERT(context);
  278.     FW_PRIV_ASSERT(gExceptionData);
  279.     FW_PRIV_ASSERT(gExceptionData->fStackTop);
  280.     FW_PRIV_ASSERT(gExceptionData->fStackBottom);
  281.  
  282.     gExceptionData->fIsUnwinding = true;
  283.  
  284.     FW_SPrivDeleteElem* last = gExceptionData->fStackBottom + context->fDeleteStackLevel;
  285.     FW_PRIV_ASSERT(gExceptionData->fStackTop >= last);
  286.     
  287.     while (gExceptionData->fStackTop > last)
  288.     {
  289.         DeleteElem_DestroyObject(gExceptionData->fStackTop-1);
  290.         // The destructor of the deleted object will have popped one or more elements 
  291.         // off of the stack so fStackTop will point to the new stack top.
  292.     }
  293.  
  294.     FW_PRIV_ASSERT(gExceptionData->fStackTop == last);
  295.     gExceptionData->fIsUnwinding = false;
  296. }
  297.  
  298. inline void TryBlockContext_LongJump(FW_SPrivTryBlockContext* context)
  299. {
  300.      (*context->fJumpProc)(*context->fJumpBuffer, 1);
  301.      FW_PRIV_ASSERT(false);    // should never return from above function
  302. }
  303.  
  304. static char* gContextName = "TryBlockContext";
  305.  
  306. void FW_PrivTryBlockContext_Init(FW_SPrivTryBlockContext *self, jmp_buf buffer, FW_PrivLongJumpProc jumpProc)
  307. {
  308.     if (!gExceptionData->fStackBottom)
  309.         InitializeExceptionGlobals(gExceptionData, self);
  310.  
  311.     self->fPriorStackBase = gExceptionData->fCallStackBase;
  312.     self->fPriorProcessBase = gExceptionData->fProcessStackBase;
  313.  
  314.     void* currentStackBase = LMGetCurStackBase();
  315.     if (currentStackBase != gExceptionData->fProcessStackBase)
  316.     {
  317.         // We've been called from a different process 
  318.         // (which might happen during drag&drop and other? scenarios)
  319.         gExceptionData->fCallStackBase = self;
  320.         gExceptionData->fProcessStackBase = currentStackBase;
  321.     }
  322.  
  323.     self->fJumpBuffer = (jmp_buf *) buffer;
  324.     self->fJumpProc = jumpProc;
  325.     self->fDeleteStackLevel = gExceptionData->fStackTop - gExceptionData->fStackBottom;
  326.     self->fPriorContext = FW_PrivTryBlockContext_MakeCurrent(self);
  327. #ifdef FW_DEBUG
  328.     FW_AutoConstructed(self, sizeof(FW_SPrivTryBlockContext), FW_PrivTryBlockContext_Destroy, gContextName);
  329. #else
  330.     FW_AutoConstructed(self, sizeof(FW_SPrivTryBlockContext), FW_PrivTryBlockContext_Destroy);
  331. #endif
  332. }
  333.  
  334. void FW_PrivTryBlockContext_Destroy(void* context)
  335. {
  336. #ifdef FW_DEBUG
  337.     FW_AutoDestructed(context, FW_PrivTryBlockContext_Destroy, gContextName);
  338. #else
  339.     FW_AutoDestructed(context);
  340. #endif
  341.     FW_SPrivTryBlockContext* self = (FW_SPrivTryBlockContext*) context;
  342.     
  343.     gExceptionData->fCallStackBase = self->fPriorStackBase;
  344.     gExceptionData->fProcessStackBase = self->fPriorProcessBase;
  345.  
  346. #ifdef FW_DEBUG
  347.     while (gExceptionData->fStackTop != gExceptionData->fStackBottom+self->fDeleteStackLevel)
  348.     {
  349.         FW_SPrivDeleteElem* elem = --gExceptionData->fStackTop;
  350.         if (elem->fObject != 0)
  351.         {
  352.             // If one of the above test fails, one or more objects were not removed off of
  353.             // the delete stack by the time the try block went out of scope.  This is almost
  354.             // certainly because some class has an FW_END_CONSTRUCTOR without a matching FW_START_DESTRUCTOR.
  355.             char message[256];
  356.             sprintf(message, "The autodestruct class %s is missing an FW_START_DESTRUCTOR", elem->fName);
  357.             FW_DEBUG_MESSAGE(message);
  358.         }
  359.     }
  360. #endif
  361.     
  362.     FW_PRIV_ASSERT(self->fDeleteStackLevel == gExceptionData->fStackTop - gExceptionData->fStackBottom);
  363.     FW_SPrivTryBlockContext* priorContext = FW_PrivTryBlockContext_MakeCurrent(self->fPriorContext);
  364.     FW_PRIV_ASSERT(priorContext == self);
  365. }
  366.  
  367. FW_SPrivTryBlockContext* FW_PrivTryBlockContext_MakeCurrent(FW_SPrivTryBlockContext* context)
  368. {
  369.     FW_SPrivTryBlockContext *tmpContext = gExceptionData->fCurrentContext;
  370.     gExceptionData->fCurrentContext = context;
  371.     return tmpContext;
  372. }
  373.  
  374. //========================================================================================
  375. // FW_SPrivWatcher Functions
  376. //========================================================================================
  377.  
  378. static void Watcher_Push(FW_SPrivWatcher *self, void* obj, size_t size)
  379. {
  380.     self->fObject = obj;
  381.     self->fLimit = (char*)obj + size;
  382.     self->fNext = gExceptionData->fWatchList;
  383.     gExceptionData->fWatchList = self;
  384. }
  385.  
  386. static char* gWatcherName = "Watcher";
  387.  
  388. void FW_PrivWatcher_Init(FW_SPrivWatcher* self, FW_PrivDeleteProc deleter)
  389. {
  390.     self->fNext = 0;
  391.     self->fObject = 0;
  392.     self->fLimit = 0;
  393.     self->fDeleter = deleter;
  394. #ifdef FW_DEBUG
  395.     self->fDeleteElems = 0;
  396.     FW_AutoConstructed(self, sizeof(FW_SPrivWatcher), FW_PrivWatcher_Destroy, gWatcherName);
  397. #else
  398.     FW_AutoConstructed(self, sizeof(FW_SPrivWatcher), FW_PrivWatcher_Destroy);
  399. #endif
  400. }
  401.  
  402. void* FW_PrivWatcher_Pop()
  403. {
  404.     FW_PRIV_ASSERT(gExceptionData);
  405.     FW_PRIV_ASSERT(gExceptionData->fWatchList);
  406.     FW_PRIV_ASSERT(gExceptionData->fWatchList->fObject);
  407.     void* result = gExceptionData->fWatchList->fObject;
  408.     
  409.     if (gExceptionData->fCurrentContext)
  410.         DeleteStack_ClearHeapObjectElements();
  411.  
  412.     gExceptionData->fWatchList->fObject = 0;
  413.     gExceptionData->fWatchList = gExceptionData->fWatchList->fNext;
  414.     return result;
  415. }
  416.  
  417. void FW_PrivWatcher_Destroy(void* self)
  418. {
  419. #ifdef FW_DEBUG
  420.     FW_AutoDestructed(self, FW_PrivWatcher_Destroy, gWatcherName);
  421. #else
  422.     FW_AutoDestructed(self);
  423. #endif
  424.     if (gExceptionData->fWatchList == self)
  425.     {
  426.         FW_SPrivWatcher* me = (FW_SPrivWatcher*) self;
  427.         FW_PRIV_ASSERT(me->fObject);
  428.         FW_PrimitiveFreeBlock(me->fObject);
  429.         gExceptionData->fWatchList = me->fNext;
  430.     }
  431. }
  432.  
  433. void* FW_PrivWatcher_New(void* p, size_t size, FW_SPrivWatcher* self)
  434. {
  435.     Watcher_Push(self, p, size);
  436.     return p;
  437. }
  438.  
  439. //========================================================================================
  440. // Global Functions
  441. //========================================================================================
  442.  
  443. void FW_AutoConstructed(void* object, size_t, FW_PrivDestroyProc destroyer _FW_NAME_PARAMETER)
  444. {
  445.     if (FW_gInStaticInit == kMagicCookie)
  446.         return;
  447.  
  448.     FW_PRIV_ASSERT(gExceptionData);
  449.     FW_PRIV_ASSERT(("Error: an autodestruct object constructed without an enclosing a FW_TRY block", gExceptionData->fCurrentContext!=NULL));
  450.  
  451.     FW_PRIV_ASSERT(gExceptionData->fStackTop);
  452.     FW_PRIV_ASSERT(gExceptionData->fStackBottom);
  453.  
  454.     if (TrackThisObject(object))
  455.     {
  456. #ifdef FW_DEBUG
  457.         DeleteStack_Push(object, destroyer, name);
  458. #else
  459.         DeleteStack_Push(object, destroyer);
  460. #endif
  461.     }
  462.     else
  463.     {
  464. #ifdef FW_DEBUG
  465.         char buffer[256];
  466.         sprintf(buffer, "Why is an autodestruct object of class %s being constructed in a bad context?  See FWExcImp.cpp for possible causes.", name);
  467.         FW_DEBUG_MESSAGE(buffer);
  468.         // Possible causes:
  469.         // 1) The autodestruct object is a member of another object which itself 
  470.         // is not an autodestruct object. This other object is being
  471.         // allocated via new, not FW_NEW.  If so, this is a programmer error.
  472.         // Make the other object's class be autodestruct, and use FW_NEW.
  473.         // 2) This is a static object of a shared library that has just been
  474.         // loaded, and static constructors are being executed.  We have attempted
  475.         // to detect this condition and not report it as an error, but may not
  476.         // have eliminated it for all environments.  If this is the case, it
  477.         // is not a programming error.  You can safely continue execution.
  478.         // Note: if you move this code into an application (as opposed to a 
  479.         // shared library), you may need to set FW_gInStaticInit to zero
  480.         // in your main() to inform this subsystem that static initialization
  481.         // is complete.
  482.         // 3) A case similar to 2) is possible.  If a static autodestruct object
  483.         // is declared in a function, the compiler is not obligated to initialize
  484.         // it at static initialization time, it can (and should) wait until the function is
  485.         // first called.  This case is very difficult to detect (without help
  486.         // from the compiler!).  It is not a programming error, and it is safe
  487.         // to continue excecution.
  488. #endif
  489.     }
  490. }
  491.  
  492. void FW_AutoDestructed(
  493.     void* object
  494. #ifdef FW_DEBUG
  495.     , FW_PrivDestroyProc destroyer
  496.     , char* name
  497. #endif
  498.     )
  499. {
  500.     if (FW_gInStaticInit == kMagicCookie)
  501.         return;
  502.  
  503.     FW_PRIV_ASSERT(gExceptionData);
  504.     FW_PRIV_ASSERT(gExceptionData->fStackTop);
  505.     FW_PRIV_ASSERT(gExceptionData->fStackBottom);
  506.     
  507.     if (gExceptionData->fCurrentContext)
  508.     {
  509.         if (TrackThisObject(object))
  510.         {
  511.             FW_SPrivDeleteElem* elem = gExceptionData->fStackTop-1;
  512.             FW_SPrivDeleteElem* last = gExceptionData->fStackBottom
  513.                                      + gExceptionData->fCurrentContext->fDeleteStackLevel;
  514.             
  515.             // Search for the correct entry on the delete stack.
  516.             // Normally this will be the first entry, but not always.
  517.             while (elem>=last && elem->fObject!=object)
  518.                 --elem;
  519.             
  520. #ifdef FW_DEBUG
  521.             // If any of the following tests fail, we didn't find the object being destructed
  522.             // in our delete stack.  Probable cause is a FW_START_DESTRUCTOR without
  523.             // matching FW_END_CONSTRUCTOR.
  524.             char message[256];
  525.             if (elem<last)
  526.             {
  527.                 sprintf(message, "The autodestruct class %s is missing an FW_END_CONSTRUCTOR.", name);
  528.                 FW_DEBUG_MESSAGE(message);
  529.             }
  530.             else if (elem->fObject != object || elem->fDestroyer != destroyer)
  531.             {
  532.                 sprintf(message, "An autodestruct class is missing an FW_END_CONSTRUCTOR or FW_START_DESTRUCTOR. Likely candidates: %s and %s respectively.", name, elem->fName);
  533.                 FW_DEBUG_MESSAGE(message);
  534.             }
  535. #endif
  536.             
  537.             // Objects aren't necessarily removed in the order they were pushed.
  538.             // We simply clear entries on the stack, and then reset the stack top
  539.             // to be one past the last active item on the stack.
  540.             elem->fObject = 0;
  541.             DeleteStack_ResetTop();
  542.         }
  543.     }
  544. }
  545.  
  546. void* FW_PrivGetThrownException()
  547. {
  548.     return gExceptionData->fThrownException.fAddress;
  549. }
  550.  
  551. void FW_PrivCaughtException(void* exception, size_t size, FW_SClassInfoPtr itsClass, FW_PrivDestroyProc itsDestroyer, FW_PrivCloneProc itsCloner)
  552. {
  553.     // We've caught an exception by value, and possibly sliced the object.
  554.     // We like to not clear the fThrownException in case we do a FW_THROW_SAME.
  555.     // Unfortunately, we don't work that way right now !!! •••JEL
  556.     FW_PRIV_ASSERT(gExceptionData);
  557.     FW_PRIV_ASSERT(!gExceptionData->fCaughtException.fAddress);
  558.     ExceptionInfo_Set(gExceptionData->fCaughtException, exception, size, itsClass, itsDestroyer, itsCloner);
  559.     ExceptionInfo_Clear(gExceptionData->fThrownException);
  560. }
  561.  
  562. void FW_PrivCaughtNoInstanceException()
  563. {
  564.     // We've caught an exception by name only
  565.     FW_PRIV_ASSERT(gExceptionData);
  566.     gExceptionData->fCaughtException = gExceptionData->fThrownException;
  567.     ExceptionInfo_Clear(gExceptionData->fThrownException);
  568. }
  569.  
  570. void FW_PrivCaughtEverythingException()
  571. {
  572.     // We've caught any kind of exception
  573.     FW_PrivCaughtNoInstanceException();
  574. }
  575.  
  576. void FW_PrivCaughtReferenceException()
  577. {
  578.     // We've caught an exception by reference
  579.     FW_PrivCaughtNoInstanceException();
  580. }
  581.  
  582. void FW_PrivCatchCleanup()
  583. {
  584.     FW_PRIV_ASSERT(gExceptionData);
  585.     ExceptionInfo_DestroyException(gExceptionData->fCaughtException);
  586.     
  587.     // The thrown exception already be cleared
  588.     FW_PRIV_ASSERT(gExceptionData->fThrownException.fAddress == 0);
  589. }
  590.  
  591. void FW_PrivKeepThrowing()
  592. {
  593.     // An exception didn't match any of the Catch clauses, so it
  594.     // needs to be propogated up to the next try block
  595.     FW_PRIV_ASSERT(gExceptionData);
  596.     FW_PRIV_ASSERT(gExceptionData->fExceptionBuffer);
  597.     FW_PRIV_ASSERT(gExceptionData->fExceptionBuffer == gExceptionData->fThrownException.fAddress);
  598.  
  599.     FW_SPrivTryBlockContext *context = gExceptionData->fCurrentContext;
  600.     FW_PRIV_ASSERT(context != NULL);
  601.     
  602.     TryBlockContext_UnwindStack(context);
  603.     TryBlockContext_LongJump(context); // LongJump doesn't return
  604. }
  605.  
  606.  
  607. FW_Boolean FW_PrivCanCatchThisException(FW_SClassInfoPtr targetClass)
  608. {
  609.     FW_PRIV_ASSERT(gExceptionData);
  610.     
  611.     void* address = gExceptionData->fThrownException.fAddress;
  612.     FW_PRIV_ASSERT(address);
  613.     
  614.     FW_SClassInfoPtr fromClass = gExceptionData->fThrownException.fClass;
  615.     
  616.     return 0 != FW_PrivDynamicCast(address, fromClass, fromClass, targetClass);
  617. }
  618.  
  619. void FW_PrivThrow(void* exception, size_t size, FW_SClassInfoPtr itsClass, FW_PrivDestroyProc itsDestroyer, FW_PrivCloneProc itsCloner)
  620. {
  621.     FW_PRIV_ASSERT(exception);
  622.     FW_PRIV_ASSERT(size);
  623.     FW_PRIV_ASSERT(itsClass);
  624.     FW_PRIV_ASSERT(itsDestroyer);
  625.     FW_PRIV_ASSERT(itsCloner);
  626.  
  627.     FW_PRIV_ASSERT(gExceptionData);
  628.     FW_PRIV_ASSERT(gExceptionData->fExceptionBuffer);
  629.     FW_PRIV_ASSERT(gExceptionData->fExceptionBufferSize);
  630.     FW_PRIV_ASSERT(size <= gExceptionData->fExceptionBufferSize);
  631.  
  632.     FW_PRIV_ASSERT(!gExceptionData->fIsUnwinding);
  633.     if (gExceptionData->fIsUnwinding)
  634.     {
  635.         // Can't throw exception while unwinding stack from previous exception
  636.         DoTerminate();
  637.     }
  638.  
  639.     // fThrownException is non-null only between throw and catch
  640.     // If it is non-null on entry to PrivThrow then something is horribly wrong
  641.     FW_PRIV_ASSERT(gExceptionData->fThrownException.fAddress == NULL);
  642.     
  643.     FW_SPrivTryBlockContext *context = gExceptionData->fCurrentContext;
  644.     if (context == NULL)
  645.         DoTerminate();
  646.  
  647.     // If we're in a catch block and we throw then there
  648.     // will be a caught exception that must be destroyed before
  649.     // we write over it with the new exception.
  650.     if (gExceptionData->fCaughtException.fAddress != NULL)
  651.         ExceptionInfo_DestroyException(gExceptionData->fCaughtException);
  652.  
  653.     // Copy the original into our exception buffer
  654.     (*itsCloner)(exception, gExceptionData->fExceptionBuffer, size);
  655.  
  656.     // We have copied the original so delete it.
  657.     (*itsDestroyer)(exception);
  658.  
  659.     // We have a thrown exception so point gThrownException at it.
  660.     ExceptionInfo_Set(gExceptionData->fThrownException, 
  661.                         gExceptionData->fExceptionBuffer, 
  662.                         size, 
  663.                         itsClass, 
  664.                         itsDestroyer, 
  665.                         itsCloner);
  666.  
  667.     TryBlockContext_UnwindStack(context);
  668.     TryBlockContext_LongJump(context);    // doesn't return
  669. }
  670.  
  671. void FW_PrivThrowSame()
  672. {
  673.     FW_PRIV_ASSERT(gExceptionData);
  674.     FW_PRIV_ASSERT(gExceptionData->fExceptionBuffer);
  675.  
  676.     FW_PRIV_ASSERT(gExceptionData->fCaughtException.fAddress);
  677.     if (gExceptionData->fCaughtException.fAddress == NULL)
  678.     {
  679.         // can't rethrow if no exception is in scope
  680.         DoTerminate();
  681.     }
  682.  
  683.     FW_PRIV_ASSERT(!gExceptionData->fIsUnwinding);
  684.     if (gExceptionData->fIsUnwinding)
  685.     {
  686.         // Can't throw exception while unwinding stack from previous exception
  687.         DoTerminate();
  688.     }
  689.  
  690.     FW_SPrivTryBlockContext *context = gExceptionData->fCurrentContext;
  691.     FW_PRIV_ASSERT(context != NULL);
  692.     if (context == NULL)
  693.         DoTerminate();
  694.  
  695.     gExceptionData->fThrownException = gExceptionData->fCaughtException;
  696.     ExceptionInfo_Clear(gExceptionData->fCaughtException);
  697.  
  698.     TryBlockContext_UnwindStack(context);
  699.     TryBlockContext_LongJump(context);
  700.     
  701.     // LongJump doesn't return, we should never get to here
  702.     FW_PRIV_ASSERT(false);
  703. }
  704.  
  705. //========================================================================================
  706. // CLASS FW_CThreadGuardExceptionGlobals
  707. //========================================================================================
  708.  
  709. class FW_CThreadGuardExceptionGlobals : public FW_CThreadGuard {
  710. public:
  711.     FW_CThreadGuardExceptionGlobals();
  712.  
  713.     virtual void Created(FW_ThreadID newlyCreatedThread);
  714.     virtual void Terminating(FW_ThreadID threadBeingKilled);
  715.  
  716. #ifdef    FW_BUILD_MAC
  717.     virtual void Switch(FW_ThreadID aThread, FW_Boolean switchingIn);
  718.  
  719. private:
  720.     ThreadID fMainThread;
  721.     FW_SExceptionGlobals *fMainThreadGlobals;
  722. #endif
  723.  
  724. private:
  725.     static FW_CThreadGuardExceptionGlobals fgThreadGuard;
  726. };
  727.  
  728.  
  729. #ifdef    FW_BUILD_MAC
  730. //----------------------------------------------------------------------------------------
  731. // FW_CThreadGuardExceptionGlobals::fgThreadGuard
  732. //    This instance is used strictly for the side-effect of its construction.  Because only 
  733. //    the Mac has thread safety problems, only create it for the Mac.
  734. //----------------------------------------------------------------------------------------
  735.  
  736. FW_CThreadGuardExceptionGlobals FW_CThreadGuardExceptionGlobals::fgThreadGuard;
  737. #endif
  738.  
  739.  
  740. //----------------------------------------------------------------------------------------
  741. // FW_CThreadGuardExceptionGlobals::FW_CThreadGuardExceptionGlobals
  742. //----------------------------------------------------------------------------------------
  743.  
  744. FW_CThreadGuardExceptionGlobals::FW_CThreadGuardExceptionGlobals() :
  745.     FW_CThreadGuard(kSwitchInThreadEvent)
  746. {
  747. #ifdef    FW_BUILD_MAC
  748.     fMainThreadGlobals = &gMainThreadExceptionData;
  749.  
  750.     // If the thread manager is present, associate the default globals with the
  751.     // ThreadID of the main thread.
  752.     long response;
  753.     OSErr anErr = Gestalt(gestaltThreadMgrAttr, &response);
  754.  
  755.     if (anErr == noErr && (response & (1 << gestaltThreadMgrPresent)))
  756.     {
  757.         // [jkp] - Check the address.  We will use weak links in the SOM future 
  758.         // and want to be sure that the routine was loaded.
  759. #if GENERATINGPOWERPC
  760.         if (&GetCurrentThread != 0)
  761.         {
  762.             anErr = GetCurrentThread(&fMainThread);
  763.             if (anErr != noErr)
  764.                 ::DebugStr("\pGetCurrentThread failed");
  765.         }
  766. #endif
  767.     }
  768. #endif
  769. }
  770.  
  771.  
  772. //----------------------------------------------------------------------------------------
  773. // FW_CThreadGuardExceptionGlobals::Created
  774. //----------------------------------------------------------------------------------------
  775.  
  776. void FW_CThreadGuardExceptionGlobals::Created(FW_ThreadID newlyCreatedThread)
  777. {
  778. #ifdef    FW_BUILD_MAC
  779.     char *exceptionGlobals = (char *)FW_PrimitiveAllocateBlock(sizeof(FW_SExceptionGlobals));
  780.     const char *limit = exceptionGlobals + sizeof(FW_SExceptionGlobals);
  781.  
  782.     AddThreadInfo(newlyCreatedThread, exceptionGlobals);
  783.  
  784.     while (exceptionGlobals < limit)
  785.         *exceptionGlobals++ = 0;
  786.         
  787. //    InitializeExceptionGlobals(gExceptionData);
  788. //    Don't explicitly initialize globals here. By deferring initialization
  789. //    until FW_PrivTryBlockContext_Init time we have a little more information
  790. //    available to help us choose a good value for fCallStackBase.
  791. #endif
  792. }
  793.  
  794.  
  795. //----------------------------------------------------------------------------------------
  796. // FW_CExceptionTaskGlobals::Terminating
  797. //----------------------------------------------------------------------------------------
  798.  
  799. void FW_CThreadGuardExceptionGlobals::Terminating(FW_ThreadID threadBeingKilled)
  800. {
  801. #ifdef    FW_BUILD_MAC
  802.     if (threadBeingKilled != fMainThread)
  803.     {
  804.         FW_SExceptionGlobals *exceptionGlobals = (FW_SExceptionGlobals *)GetThreadInfo(threadBeingKilled);
  805.  
  806.         FinalizeExceptionGlobals(exceptionGlobals);
  807.         FW_PrimitiveFreeBlock(exceptionGlobals);
  808.         RemoveThreadInfo(threadBeingKilled);
  809.     }
  810. #endif
  811. }
  812.  
  813.  
  814. #ifdef    FW_BUILD_MAC
  815. //----------------------------------------------------------------------------------------
  816. // FW_CExceptionTaskGlobals::Switch
  817. //----------------------------------------------------------------------------------------
  818.  
  819. void FW_CThreadGuardExceptionGlobals::Switch(FW_ThreadID aThread, FW_Boolean)
  820. {
  821.     if (aThread == fMainThread)
  822.     {
  823.         gExceptionData = fMainThreadGlobals;
  824.     }
  825.     else
  826.     {
  827.         FW_SExceptionGlobals *exceptionGlobals = (FW_SExceptionGlobals *)GetThreadInfo(aThread);
  828.     
  829.         if (exceptionGlobals != 0)
  830.             gExceptionData = exceptionGlobals;
  831.         else
  832.             ::DebugStr("\pNull exception globals for thread");
  833.     }
  834. }
  835. #endif
  836.  
  837.  
  838. #endif
  839.